package com.yzh.pinboard.BoardView;

/**
 * Created by Rex on 2017/2/21.
 */

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.DashPathEffect;
import android.graphics.Paint;
import android.graphics.PathEffect;
import android.graphics.Rect;
import android.os.Handler;
import android.os.Message;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewTreeObserver;
import android.widget.Toast;

import com.yzh.pinboard.CustomView.BoardGroupView;

import java.util.ArrayList;
import java.util.List;
import java.util.UUID;

//import pinboard.dataLayer.net.ActionCallback;
//import pinboard.dataLayer.net.BaseClientPipe;
//import pinboard.dataLayer.net.ClientPipeListener;
//import pinboard.dataLayer.net.InstructClientPipe;
//import pinboard.dataLayer.net.NetAddressInfo;
//import pinboard.dataLayer.net.PipeContext;

import static com.yzh.pinboard.BoardView.ClipOperate.drawClip;

public class PinBoardLine extends View {

    //3个图层
    private Bitmap mBitmap = null;
    private Bitmap eraserBitmap = null;
    private Bitmap elementBitmap = null;
    private Bitmap writingBitmap = null;
    private Bitmap backGroundBitmap = null;

    //2个画笔
    private Paint strokePaint = new Paint();
    private Paint pSelected = new Paint();

    private int boardWidth = 0;
    private int boardHeight = 0;
    private Clip showClip;
    private ClipGroup showGroup;
    private List<ClipGroup> allGroups;

    private Stroke writingStroke;
    //private InstructClientPipe instructClientPipe;


    private List<Stroke> selectedStroke = new ArrayList<>();
    private List<Stroke> selectedCopyStroke = new ArrayList<>();
    private List<Stroke> selectedCopyWriteStroke = new ArrayList<>();
    private List<Stroke> writingStrokes = new ArrayList<>();

    private List<Stroke> newSendStroke = new ArrayList<>();
    private List<Stroke> deleteSendStroke = new ArrayList<>();
    private List<Point> points = new ArrayList<>();
    private List<List<Point>> morePointIDs = new ArrayList<>();

    private int mode = 1;
    private int drawPointID = -1;
    private float selectedScale = 1f;
    private int selectedPointID = -1;
    private boolean isFirstLoad = false;
    public BoardGroupView parentView;

    public PinBoardLine(Context context) {
        super(context);

        //mContext = context;
        this.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver
                .OnGlobalLayoutListener() {
            //view布局完成回调函数获取整体高宽
            @Override
            public void onGlobalLayout() {
                // TODO Auto-generated method stub
                if (mBitmap == null) {
                    newShow();
                }
            }
        });
        //test socket
    }

    public PinBoardLine(Context context, AttributeSet attrs) {
        super(context, attrs);
        //mContext = context;
        this.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver
                .OnGlobalLayoutListener() {
            //view布局完成回调函数获取整体高宽
            @Override
            public void onGlobalLayout() {
                // TODO Auto-generated method stub
                if (mBitmap == null) {
                    newShow();
                }
            }
        });
    }

    private Handler handler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case 10:
                    invalidate();
                    //Toast.makeText(getContext(), "1234", Toast.LENGTH_SHORT);
                    break;
                case 9:
                    ClipOperate.addUndo(showClip);
                    break;
            }
        }
    };

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        if (event.getPointerCount() == 1) {
            this.onePointAction(event);
        } else {
            //isMoreFinger=true;
            this.morePointAction(event);
        }
        return super.onTouchEvent(event);
    }


    //<editor-fold desc="多点触控效果">

    private void morePointAction(MotionEvent event) {
        if (mode != 4) {
            this.drawUp();
            ClipOperate.clearBitmap(this.eraserBitmap);
        }
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                this.savePoints(event);
                break;
            case MotionEvent.ACTION_MOVE:
                this.savePoints(event);
                this.customEvents(this.morePointIDs);
                break;
            case MotionEvent.ACTION_UP:
                this.morePointIDs.clear();
                break;
        }
    }

    private void savePoints(MotionEvent event) {
        List<Point> points = new ArrayList<>();
        Point point;
        for (int i = 0; i < event.getPointerCount(); i++) {
            point = new Point();
            point.X = event.getX(i);
            point.Y = event.getY(i);
            point.pointId = event.getPointerId(i);
            points.add(point);
        }
        point = new Point();
        point.X = event.getX();
        point.Y = event.getY();
        this.points.add(point);
        this.morePointIDs.add(points);
    }

    private void customEvents(List<List<Point>> morePoints) {
        if (morePoints.size() < 2) {
            return;
        }
        List<Point> points1 = morePoints.get(morePoints.size() - 1);
        List<Point> points2 = morePoints.get(morePoints.size() - 2);
        if (points1.size() < 2 || points2.size() < 2) {
            return;
        }

        float maxD = -1000000;
        float minD = 1000000;

        float maxX = 0;
        float maxY = 0;

        Point ponitMax = new Point();
        Point pointMin = new Point();

        for (int i = 0; i < points1.size(); i++) {
            Point point1 = points1.get(i);
            Point point2 = ClipOperate.searchPoint(points2, point1.pointId);
            if (point2 == null) {
                continue;
            }
            if (maxD < ClipOperate.getAbsoluteValue(point1, point2)) {
                maxD = ClipOperate.getAbsoluteValue(point1, point2);
                ponitMax = point1;
                maxX = point1.X - point2.X;
                maxY = point1.Y - point2.Y;
            }
            if (minD > ClipOperate.getAbsoluteValue(point1, point2)) {
                minD = ClipOperate.getAbsoluteValue(point1, point2);
                pointMin = point1;
            }
        }

        if (maxD == 0 || minD == 0) {
            return;
        }
        if (maxD > 0 && minD < 0) {
            float scale = (maxD - minD) / 1000;
            if (pointMin.X - ponitMax.X > 0) {
                scale = 0 - scale;
            }
            if (mode == 4 && this.selectedStroke.size() > 0) {
                this.selectedScale = this.selectedScale + scale;
                ClipOperate.scaleStrokeWithLimit(this.selectedStroke, this.showClip, this.selectedCopyStroke, this.selectedScale);
                ClipOperate.scaleStrokeWithoutLimit(this.writingStrokes, this.showClip, this.selectedCopyWriteStroke, this.selectedScale);
                drawClip(this.showClip, this.mBitmap, this.strokePaint);
                ClipOperate.clearBitmap(this.writingBitmap);
                ClipOperate.drawPoints(writingStroke.points, this.showClip, this.writingBitmap, this.pSelected, true);
            } else {
                //ClipOperate.drawElement(this.showClip, this.elementBitmap);
                ClipOperate.scaleClip(this.showClip, scale);
                this.drawClipWithElement();
            }
        }

        if (maxD * minD > 0) {
            //多指不移动选中的线条
//            if (mode == 4 && this.selectedStroke.size() > 0) {
//                this.moveSelectedStrokes(this.selectedStroke, maxX, maxY);
//                this.selectedCopyStroke = (List<Stroke>) CommonMethod.cloneObject(this.selectedStroke);
//            } else {
//                ClipOperate.setOffset(this.showClip, maxX, maxY);
//                this.drawClip(this.showClip);
//            }
            ClipOperate.setOffset(this.showClip, maxX, maxY);
            this.drawClipWithElement();
        }
        this.refreshUI();
    }

    //</editor-fold>

    //<editor-fold desc="单点触控效果">

    private void onePointAction(MotionEvent event) {
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                switch (mode) {
                    case 1:
                        this.drawDown(event);
                        break;
                    case 2:
                        this.eraserMove(event);
                        break;
                    case 3:
                        this.clipMoveDown(event);
                        break;
                    case 4:
                        this.drawSelectedDown(event);
                        break;
                }
                break;
            case MotionEvent.ACTION_MOVE:
                switch (mode) {
                    case 1:
                        this.drawMove(event);
                        break;
                    case 2:
                        this.eraserMove(event);
                        break;
                    case 3:
                        this.clipMove(event);
                        break;
                    case 4:
                        this.drawSelectedMove(event);
                        break;
                }
                break;
            case MotionEvent.ACTION_UP:
                switch (mode) {
                    case 1:
                        this.drawUp();
                        break;
                    case 2:
                        this.eraserUp();
                        break;
                    case 3:
                        this.clipModeUp();
                        break;
                    case 4:
                        this.drawSelectedUp(event);
                        break;
                }
                this.morePointIDs.clear();
                break;
        }

    }

    //画笔的写作
    private void drawDown(MotionEvent event) {
        //Toast.makeText(this.getContext(),String.valueOf(event.getSize()), Toast.LENGTH_SHORT).show();

        drawPointID = event.getPointerId(0);
        Point startPoint = new Point();
        writingStroke = new Stroke();
        writingStroke.width = strokePaint.getStrokeWidth();
        writingStroke.color = strokePaint.getColor();
        writingStroke.id = UUID.randomUUID().toString();
        startPoint.Y = event.getY();
        startPoint.X = event.getX();
        ClipOperate.calculatePointByScale(this.showClip, startPoint);
        writingStroke.points.add(startPoint);
    }

    private void drawMove(MotionEvent event) {
        this.writing(event);
        ClipOperate.clearBitmap(this.writingBitmap);
        ClipOperate.drawPoints(writingStroke.points, this.showClip, this.writingBitmap, this.strokePaint, false);
        //this.drawPoints(writingStroke.points, writingCanvas, this.strokePaint, false);
        this.refreshUI();
    }

    private void drawUp() {
        //Toast.makeText(this.getContext(),"show drawup", Toast.LENGTH_SHORT).show();
        points.clear();
        this.drawPointID = -1;
        if (writingStroke.points.size() == 2) {
            if (writingStroke.points.get(0).X == writingStroke.points.get(0).X && writingStroke
                    .points.get(1).Y == writingStroke.points.get(1).Y) {
                return;
            }
        }
        if (writingStroke.points.size() < 2) {
            return;
        }
        ClipOperate.clearBitmap(this.writingBitmap);
        this.showClip.strokes.add(writingStroke);

        Thread add = new Thread() {
            public void run() {
                addundo();
            }
        };
        add.start();
        //ClipOperate.addUndo(this.showClip);
        drawClip(this.showClip, this.mBitmap, this.strokePaint);
        this.refreshUI();

    }

    //选中元素的下笔
    private void drawSelectedDown(MotionEvent event) {
        Point startPoint = new Point();
        selectedPointID = event.getPointerId(0);
        if (event.findPointerIndex(selectedPointID) == -1) {
            return;
        }
        startPoint.Y = event.getY(event.findPointerIndex(selectedPointID));
        startPoint.X = event.getX(event.findPointerIndex(selectedPointID));
        this.points.add(startPoint);

        if (this.selectedStroke.size() == 0) {
            drawPointID = event.getPointerId(0);
            writingStroke = new Stroke();
            writingStroke.id = UUID.randomUUID().toString();
            ClipOperate.calculatePointByScale(this.showClip, startPoint);
            writingStroke.points.add(startPoint);
        }
    }

    private void drawSelectedMove(MotionEvent event) {
        if (event.findPointerIndex(selectedPointID) == -1) {
            return;
        }
        Point startPoint = new Point();
        startPoint.Y = event.getY(event.findPointerIndex(selectedPointID));
        startPoint.X = event.getX(event.findPointerIndex(selectedPointID));
        this.points.add(startPoint);
        if (this.selectedStroke.size() == 0) {
            this.writing(event);
            ClipOperate.clearBitmap(this.writingBitmap);
            ClipOperate.drawPoints(writingStroke.points, this.showClip, this.writingBitmap, this.pSelected, false);
            this.refreshUI();
        } else {
            Point movePoint = new Point();
            movePoint.X = points.get(points.size() - 1).X - points.get(points.size() - 2).X;
            movePoint.Y = points.get(points.size() - 1).Y - points.get(points.size() - 2).Y;
            this.moveSelectedStrokes(this.selectedStroke, movePoint.X, movePoint.Y);
            ClipOperate.moveStrokeWithLimit(this.selectedCopyStroke, movePoint, this.showClip);
            ClipOperate.moveStrokeWithoutLimit(this.selectedCopyWriteStroke, movePoint);
        }
    }

    private void drawSelectedUp(MotionEvent event) {
        if (this.points.size() == 1) {
            ClipOperate.clearBitmap(this.writingBitmap);
            this.selectedStroke.clear();
            this.writingStrokes.clear();
            this.refreshUI();
            return;
        } else if (this.selectedStroke.size() == 0) {
            ClipOperate.clearBitmap(this.writingBitmap);
            ClipOperate.getSelectStrokes(this.showClip, writingStroke, this.selectedStroke);
            this.selectedScale = 1f;
            if (this.selectedStroke.size() > 0) {
                this.writingStrokes.add(writingStroke);
                this.selectedCopyStroke = (List<Stroke>) CommonMethod.cloneObject(this.selectedStroke);
                this.selectedCopyWriteStroke = (List<Stroke>) CommonMethod.cloneObject(this.writingStrokes);
                ClipOperate.drawPoints(writingStroke.points, this.showClip, this.writingBitmap, this.pSelected, true);
            }
            this.refreshUI();
        }
        this.points.clear();
    }

    private void moveSelectedStrokes(List<Stroke> mStroke, float offsetX, float offsetY) {
        Point movePoint = new Point();
        movePoint.X = offsetX;
        movePoint.Y = offsetY;
        ClipOperate.moveStrokeWithLimit(mStroke, movePoint, this.showClip);
        List<Stroke> wrtingStrokes = new ArrayList<>();
        wrtingStrokes.add(this.writingStroke);
        ClipOperate.moveStrokeWithoutLimit(wrtingStrokes, movePoint);
        drawClip(this.showClip, this.mBitmap, this.strokePaint);
        ClipOperate.clearBitmap(this.writingBitmap);
        ClipOperate.drawPoints(writingStroke.points, this.showClip, this.writingBitmap, this.pSelected, true);
        this.refreshUI();
    }

    //页面移动的部分
    private void clipMoveDown(MotionEvent event) {
        Point newP = new Point();
        newP.X = event.getX();
        newP.Y = event.getY();
        points.add(newP);
    }

    private void clipMove(MotionEvent event) {
        Point newP = new Point();
        newP.X = event.getX();
        newP.Y = event.getY();
        points.add(newP);
        float moveX = points.get(points.size() - 1).X - points.get(points.size()
                - 2).X;
        float moveY = points.get(points.size() - 1).Y - points.get(points.size()
                - 2).Y;
        ClipOperate.setOffset(this.showClip, moveX, moveY);
        this.drawClipWithElement();
        this.refreshUI();
    }

    private void clipModeUp() {
        this.points.clear();
    }

    //橡皮擦
    private void eraserMove(MotionEvent event) {
        ClipOperate.clearBitmap(this.eraserBitmap);
        //this.showEraserBoard(event);
        Rect cleanRect = new Rect((int) event.getX() -
                150, (int) event.getY() + 150, (int) event.getX() + 150, (int)
                event.getY() - 150);
        //ClipOperate.calculateRect(cleanRect,this.showClip);
        Paint eraserP = new Paint();
        eraserP.setStrokeWidth(3);
        eraserP.setColor(Color.BLACK);
        eraserP.setStyle(Paint.Style.STROKE);
        ClipOperate.drawRect(this.eraserBitmap, cleanRect, eraserP);
        Rect cleanNew = new Rect(cleanRect.left, cleanRect.top, cleanRect.right, cleanRect.bottom);
        ClipOperate.calculateRect(cleanNew, this.showClip);
        ClipOperate.eraserByRect(this.showClip, this.newSendStroke, this.deleteSendStroke, cleanNew);
        drawClip(this.showClip, this.mBitmap, this.strokePaint);
        this.refreshUI();
    }

    private void eraserUp() {
        ClipOperate.clearBitmap(this.eraserBitmap);

        if (this.newSendStroke.size() > 0 || this.deleteSendStroke.size() > 0) {
            ClipOperate.addUndo(this.showClip);
        }
        this.newSendStroke.clear();
        this.deleteSendStroke.clear();
        this.refreshUI();
    }

    private void writing(MotionEvent event) {
        if (event.findPointerIndex(this.drawPointID) == -1) {
            return;
        }
        Point movePoint = new Point();
        movePoint.Y = event.getY(event.findPointerIndex(this.drawPointID));
        movePoint.X = event.getX(event.findPointerIndex(this.drawPointID));
        ClipOperate.calculatePointByScale(this.showClip, movePoint);
        writingStroke.points.add(movePoint);
    }

    //</editor-fold>

    //<editor-fold desc="操作函数">

    private void newShow() {
        if (isFirstLoad) {
            return;
        }
        mBitmap = Bitmap.createBitmap(boardWidth, boardHeight, Bitmap.Config.ARGB_8888);
        eraserBitmap = Bitmap.createBitmap(boardWidth, boardHeight, Bitmap.Config.ARGB_8888);
        writingBitmap = Bitmap.createBitmap(boardWidth, boardHeight, Bitmap.Config.ARGB_8888);
        elementBitmap = Bitmap.createBitmap(boardWidth, boardHeight, Bitmap.Config.ARGB_4444);
        backGroundBitmap = Bitmap.createBitmap(boardWidth, boardHeight, Bitmap.Config.ARGB_4444);

        strokePaint = new Paint();
        strokePaint.setColor(Color.WHITE);
        strokePaint.setStrokeJoin(Paint.Join.ROUND);
        strokePaint.setStrokeCap(Paint.Cap.ROUND);
        strokePaint.setStyle(Paint.Style.STROKE);
        strokePaint.setStrokeWidth(1);
        //初始化数据结构
        showGroup = new ClipGroup(boardWidth, boardHeight);
        showClip = new Clip(boardWidth, boardHeight);
        showGroup.clips.add(showClip);
        showGroup.currentClip = showClip;
        allGroups = new ArrayList<>();
        allGroups.add(showGroup);

        pSelected.setStrokeWidth(2);
        pSelected.setColor(Color.RED);
        pSelected.setStyle(Paint.Style.STROKE);
        PathEffect effects = new DashPathEffect(new float[]{5, 5, 5, 5}, 0);
        pSelected.setPathEffect(effects);

        ClipOperate.addUndo(this.showClip);
        ClipOperate.drawBackBitmap(this.backGroundBitmap);
        invalidate();
    }

    @Override
    protected void onDraw(Canvas canvas) {
        isFirstLoad = true;
        if (mBitmap != null) {
            //elementBitmap.setWidth(this.boardWidth);
            canvas.drawBitmap(backGroundBitmap,0,0,null);
            canvas.drawBitmap(elementBitmap, 0, 0, null);
            canvas.drawBitmap(mBitmap, 0, 0, null);
            canvas.drawBitmap(eraserBitmap, 0, 0, null);
            canvas.drawBitmap(writingBitmap, 0, 0, null);
        }

    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        boardWidth = getMeasuredWidth();
        boardHeight = getMeasuredHeight();
    }

    //</editor-fold>

    //<editor-fold desc="外放接口">

//    public void connectServerSocket(String ipAddress, int port) {
//        NetAddressInfo address = new NetAddressInfo();
//        address.setHost(ipAddress);
//        address.setPort(port);
//        address.setName("Server");
//        address.setTag(null);
//
//        //STEP2 连接管道事件监听
//        ClientPipeListener listener = new ClientPipeListener() {
//            @Override
//            public void onDisconnect(BaseClientPipe clientPipe) {
//                //这里可以UI提示掉线
//                clientPipe.connect(3000, new ActionCallback<PipeContext>() {
//                    //连接完毕的回调
//                    @Override
//                    public void invoke(Object sender, PipeContext arg) {
//                    }
//                });
//            }
//
//            @Override
//            public void onMessage(BaseClientPipe baseClientPipe, byte[] bytes) {
//
//            }
//
//        };
//
//        //STEP3 连接器回调监听
//        final ActionCallback<PipeContext> connectCallback = new ActionCallback<PipeContext>() {
//            //连接完毕的回调
//            @Override
//            public void invoke(Object sender, PipeContext arg) {
//                boolean m = arg.getNetState();
//                //Message
//                //handler.sendMessage()
//                if (m) {
//                    handler.sendEmptyMessage(1);
//                }
//            }
//        };
//
//        //STEP4 开线程启动连接
//        instructClientPipe = new InstructClientPipe(address, listener);
//        new Thread(new Runnable() {
//            @Override
//            public void run() {
//                instructClientPipe.connect(3000, connectCallback);
//            }
//        }).start();
//    }

    public void setMode(int mMode) {
        this.points.clear();
        this.mode = mMode;
    }

    public Clip getShowClip() {
        return this.showClip;
    }

    public void scaleBoard(float mScale) {
        ClipOperate.scaleClip(this.showClip, mScale);
        this.drawClipWithElement();
        this.refreshUI();
    }

    public float getBoardWidth() {

        return this.boardWidth;
    }

    public float getBoarHeight() {
        return this.boardHeight;
    }

    public void setStrokeColor(int color) {
        strokePaint.setColor(color);
    }

    public void setStrokeWidth(float width) {
        strokePaint.setStrokeWidth(width);
    }

    public void changePage(int index, Context mContext) {
        int showIndex = showGroup.clips.indexOf(showClip);
        if ((showIndex == 0 && index < 0)) {
            Toast.makeText(mContext, "第一页", Toast.LENGTH_SHORT).show();
            return;
        }
        if (showIndex == showGroup.clips.size() - 1 && index > 0) {
            Toast.makeText(mContext, "最后一页", Toast.LENGTH_SHORT).show();
            return;
        }
        showIndex = showIndex + index;
        this.showClip = this.showGroup.currentClip = this.showGroup.clips.get(showIndex);
        drawClipWithElement();
        this.refreshUI();
    }

    public void undoClip() {
        ClipOperate.undo(this.showClip);
        drawClip(this.showClip, this.mBitmap, this.strokePaint);
        this.refreshUI();
    }

    public void redoClip() {
        ClipOperate.redo(this.showClip);
        drawClip(this.showClip, this.mBitmap, this.strokePaint);
        this.refreshUI();
    }

    public void newPage() {
        Clip newClip = new Clip(showGroup.groupWidth, showGroup.groupHeight);
        showGroup.clips.add(newClip);
        showGroup.currentClip = newClip;
        showClip = newClip;
        drawClipWithElement();
        this.refreshUI();
    }

    //</editor-fold>


    public void drawImage(String Path) {
        ClipElement newElement = new ClipElement();
        Bitmap eleBitmap = BitmapFactory.decodeFile(Path);
        newElement.sizeX = eleBitmap.getWidth();
        newElement.sizeY = eleBitmap.getHeight();
        newElement.elementData = eleBitmap;
        ClipOperate.addElementToClip(this.showClip, newElement);
        //this.showClip.elements.add(newElement);
        //ClipOperate.drawElement(this.showClip, this.elementBitmap);
        this.drawClipWithElement();
        //this.elementBitmap = BitmapOperate.getBitmap(this.elementBitmap, this.boardWidth, this.boardHeight);
        this.refreshUI();
    }

    private void drawClipWithElement() {
        ClipOperate.drawElement(this.showClip, this.elementBitmap);
        ClipOperate.drawClip(this.showClip, this.mBitmap, this.strokePaint);
    }

    private void refreshUI() {
        handler.sendEmptyMessage(10);
    }

    private void refreshStroke() {
        handler.sendEmptyMessage(9);
    }

    public synchronized void addundo() {
        ClipOperate.addUndo(showClip);
    }

    public void deleteAll(){
        Thread add = new Thread() {
            public void run() {
                addundo();
            }
        };
        add.start();
        this.showClip.elements.clear();
        this.showClip.strokes.clear();
        ClipOperate.clearBitmap(this.mBitmap);
        this.refreshUI();
    }
}
